#include "bind.h"
#include "scheduler.h"
#include <sstream>

ccc_bind_declspec auto scheduler_create() { return (sPointer)(new Scheduler()); }
ccc_bind_declspec auto scheduler_update(sPointer scheduler, float dt) { return ((Scheduler*)scheduler)->update(dt); }

ccc_bind_declspec auto ref_retain(sPointer ref) { return ((Ref*)ref)->retain(); }
ccc_bind_declspec auto ref_autorelease(sPointer ref) { return ((Ref*)ref)->autorelease(); }
ccc_bind_declspec auto ref_release(sPointer ref) { return ((Ref*)ref)->release(); }

struct sSchedulerNode : public Ref
{
	Scheduler* _scheduler = nullptr;
	bool _running = true;
	bool _isScheduled = false;
	int _lua_func_index = 0;
	std::string _lua_func_str;

	sSchedulerNode()
	{
		_scheduler = Scheduler::getInstance();
		_scheduler->retain();
	}

	void setScheduler(Scheduler* scheduler)
	{
		if (scheduler != _scheduler)
		{
			if (_scheduler != nullptr)
			{
				_scheduler->unscheduleAllForTarget(this);
				_scheduler->release();
			}
			_isScheduled = false;
			if (scheduler != nullptr)
			{
				scheduler->retain();
			}
			_scheduler = scheduler;
		}
	}
};

ccc_bind_declspec auto ref_create() { return (sPointer)(new sSchedulerNode()); }

ccc_bind_declspec auto ref_setScheduler(sPointer node, sPointer scheduler) { return ((sSchedulerNode*)node)->setScheduler((Scheduler*)scheduler); }

static auto ref_unschedule(sPointer node)
{
	auto n = (sSchedulerNode*)node;
	if (n->_lua_func_index != 0)
	{
		n->_scheduler->unschedule(n->_lua_func_str, n);
		n->_lua_func_str.clear();
		luaL_unref(L, LUA_REGISTRYINDEX, n->_lua_func_index);
		n->_lua_func_index = 0;
	}
}


static auto ref_schedule(sPointer node, float interval/*, unsigned int repeat*/, float delay)
{
	ref_unschedule(node);
	auto n = (sSchedulerNode*)node;
	n->_lua_func_index = luaL_ref(L, LUA_REGISTRYINDEX);
	std::ostringstream ss;
	ss << n->_lua_func_index;
	n->_lua_func_str = ss.str();
	return n->_scheduler->schedule([n](float dt)
	{
		ccc_en_lock;
		lua_rawgeti(L, LUA_REGISTRYINDEX, n->_lua_func_index);
		lua_pushnumber(L, dt);
		lua_call(L, 1, 0);
		ccc_un_lock;

	}, n, interval, (UINT_MAX - 1), delay, !n->_running, n->_lua_func_str);
}



void registerScheduler(lua_State* ccc_register_state)
{
	ccc_register_func(scheduler_create);
	ccc_register_func(scheduler_update);
	ccc_register_func(ref_retain);
	ccc_register_func(ref_autorelease);
	ccc_register_func(ref_release);
	ccc_register_func(ref_create);
	ccc_register_func(ref_setScheduler);
	ccc_register_func(ref_schedule);
	ccc_register_func(ref_unschedule);
}
